#!/usr/bin/python2.5
#
# Copyright 2009 Roman Nurik
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Defines some useful geo types such as points and boxes."""

__author__ = 'api.roman.public@gmail.com (Roman Nurik)'


class Point(object):
    """A two-dimensional point in the [-90,90] x [-180,180] lat/lon space.

    Attributes:
      lat: A float in the range [-90,90] indicating the point's latitude.
      lon: A float in the range [-180,180] indicating the point's longitude.
    """

    def __init__(self, lat, lon):
        """Initializes a point with the given latitude and longitude."""
        if -90 > lat or lat > 90:
            raise ValueError("Latitude must be in [-90, 90] but was %f" % lat)
        if -180 > lon or lon > 180:
            raise ValueError("Longitude must be in [-180, 180] but was %f" % lon)

        self.lat = lat
        self.lon = lon

    def __eq__(self, other):
        return self.lat == other.lat and self.lon == other.lon

    def __str__(self):
        return '(%f, %f)' % (self.lat, self.lon)


class Box(object):
    """A two-dimensional rectangular region defined by NE and SW points.

    Attributes:
      north_east: A read-only geotypes.Point indicating the box's Northeast
          coordinate.
      south_west: A read-only geotypes.Point indicating the box's Southwest
          coordinate.
      north: A float indicating the box's North latitude.
      east: A float indicating the box's East longitude.
      south: A float indicating the box's South latitude.
      west: A float indicating the box's West longitude.
    """

    def __init__(self, north, east, south, west):
        # TODO(romannurik): port geojs LatLonBox here
        if south > north:
            south, north = north, south

        # Don't swap east and west to allow disambiguation of
        # antimeridian crossing.

        self._ne = Point(north, east)
        self._sw = Point(south, west)

    north_east = property(lambda self: self._ne)
    south_west = property(lambda self: self._sw)

    def _set_north(self, val):
        if val < self._sw.lat:
            raise ValueError("Latitude must be north of box's south latitude")
        self._ne.lat = val
    north = property(lambda self: self._ne.lat, _set_north)

    def _set_east(self, val):
        self._ne.lat = val
    east  = property(lambda self: self._ne.lon, _set_east)

    def _set_south(self, val):
        if val > self._ne.lat:
            raise ValueError("Latitude must be south of box's north latitude")
        self._sw.lat = val
    south = property(lambda self: self._sw.lat, _set_south)

    def _set_west(self, val):
        self._sw.lon = val
    west  = property(lambda self: self._sw.lon, _set_west)

    def __eq__(self, other):
        return self._ne == other._ne and self._sw == other._sw

    def __str__(self):
        return '(N:%f, E:%f, S:%f, W:%f)' % (self.north, self.east,
                                             self.south, self.west)
